package com.example.demo.controller;

import com.baomidou.mybatisplus.extension.api.R;
import com.example.demo.service.BookService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * @author Shanks
 * @date 2020-10-26
 */
@Slf4j
@Api(value = "TransactionalController", tags = {"事务"})
@RestController
public class TransactionalController {

    @Resource
    private BookService bookService;

    /* ---------------------------------------------------------------------------------------------------- */

    /**
     * 官方文档: https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/data-access.html#tx-propagation-requires_new
     * 1.Method 2 executes in a new transaction, and the outer transaction is suspended.
     *
     * 复盘一下:
     * A-a: @Transactional(rollbackFor = Exception.class)
     * B-b: @Transactional(propagation = REQUIRES_NEW, rollbackFor = Exception.class)
     *
     * a() {
     *   doDB;
     *
     *   b();
     *
     *   throw ex;
     * }
     *
     * 关于事务的传播属性之 REQUIRES_NEW 总结:
     * 1.a方法开启一个事务, 然后在调用b方法时, b将当前a的事务挂起, 自己新开一个事务
     * 2.在b已经提交以后, a中后续的代码出现异常时, 并不影响b的事务的提交和回滚
     * 3.但是若b事务遇到异常回滚了, a不去捕获异常的话, 那么a事务也会受到影响而回滚
     */
    @ApiOperation(value = "事务测试A-a调用B-b", notes = "A异常")
    @GetMapping("/transactional/requires_new/a_ex")
    public R<String> requiresNewAndAlphaEx() {
        bookService.requiresNewAndAlphaEx();
        return R.ok("OK");
    }

    @ApiOperation(value = "事务测试A-a调用B-b", notes = "B中报异常")
    @GetMapping("/transactional/requires_new/b_ex")
    public R<String> requiresNewAndBetaEx() {
        bookService.requiresNewAndBetaEx();
        return R.ok("OK");
    }

    /* ---------------------------------------------------------------------------------------------------- */

    /**
     * A和B均插入失败
     *
     * 内层事务没异常, 但是外层事务异常了, 最终两个都没插入成功
     */
    @ApiOperation(value = "事务测试A调用B", notes = "事务测试A调用B(A在调用B之后的代码中设置异常)")
    @GetMapping("/transactional/nested/a_ex")
    public R<String> nestedAndAlphaEx() {
        bookService.nestedAndAlphaEx();
        return R.ok("OK");
    }

    /**
     * A插入成功, B插入失败
     *
     * 1.B方法中设置Exception, 不会影响到外层调用方A的事务!!
     * 2.而且接口的返回还是执行成功!!
     */
    @ApiOperation(value = "事务测试A调用B(B中设置异常)", notes = "事务测试A调用B(B中设置异常)")
    @GetMapping("/transactional/nested/b_ex")
    public R<String> nestedAndBetaEx() {
        bookService.nestedAndBetaEx();
        return R.ok("OK");
    }
}